//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================


namespace Shp.Runtime.Sitka.ServiceAgent.Managers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Practices.EnterpriseLibrary.Caching;
    using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
    using Shp.Runtime.Sitka.ServiceAgent.Entities;
    using Shp.Runtime.Sitka.ServiceGateway.SitkaClient;
    using System.Collections.ObjectModel;

    public class RepositoryCacheManager : IRepositoryCacheManager
    {
        private bool cacheEntitiesEnabled;
        private bool cacheQueriesEnabled;
        private CacheManager cacheManager;
        private ICacheItemExpiration entityExpiration;

        /// <summary>
        /// Creates a new cache manager with a specific manager (section in config), and an expiration policy for entites and queries
        /// </summary>
        /// <param name="cacheManagerName"></param>
        /// <param name="entitiesExpiration"></param>
        public RepositoryCacheManager(string cacheManagerName, ICacheItemExpiration entitiesExpiration, bool cacheEntities, bool cacheQueries)
        {
            Initialize(cacheManagerName, entitiesExpiration, cacheEntities, cacheQueries);
        }

        public RepositoryCacheManager(string cacheManagerName, ICacheItemExpiration expiration)
            : this(cacheManagerName, expiration, false, false)
        {
        }

        /// <summary>
        /// Use a specific cacheManager with no Expiration Policies
        /// </summary>
        /// <param name="cacheManagerName"></param>
        public RepositoryCacheManager(string cacheManagerName)
            : this(cacheManagerName, null, false, false)
        {
        }

        // Constructor for the default CacheManager
        public RepositoryCacheManager()
        {
            RepositoryCacheManagerConfig config = RepositoryCacheManagerConfigHandler.Configuration;
            if (config != null)
            {
                Initialize(config.CacheManager, new SlidingTime(TimeSpan.FromSeconds(config.Expiration)), config.CacheEntities, config.CacheQueries);
            }
            else
            {
                Initialize(null, null, false, false);
            }
        }

        public void AddOrUpdateEntity(string tenantId, Entity entity)
        {
            if (cacheManager != null)
            {
                if (this.cacheEntitiesEnabled == true)
                {
                    string key = GetEntityKey(tenantId, entity.Kind, entity.Id);

                    if (entityExpiration == null)
                    {
                        cacheManager.Add(key, entity);
                    }
                    else
                    {
                        cacheManager.Add(key, entity, CacheItemPriority.Normal, null, entityExpiration);
                    }
                }
            }
        }

        public Entity GetEntityFrom(string tenantId, string kind, string id)
        {
            if (cacheManager == null || this.cacheEntitiesEnabled == false)
            {
                return null;
            }

            string key = GetEntityKey(tenantId, kind, id);

            return (Entity)cacheManager[key];
        }

        public void DeleteEntity(string tenantId, string kind, string id)
        {
            if (cacheManager == null || this.cacheEntitiesEnabled == false)
            {
                return;
            }

            cacheManager.Remove(GetEntityKey(tenantId, kind, id));
        }

        public Collection<Entity> GetQueryResults(string tenantId, string queryString)
        {
            // No Cache
            if (cacheManager == null || this.cacheQueriesEnabled == false)
            {
                return null;
            }

            return (Collection<Entity>)cacheManager[GetQueryKey(tenantId, queryString)];
        }

        public void AddQueryResults(string tenantId, string queryString, Collection<Entity> list)
        {
            if (cacheManager != null && this.cacheQueriesEnabled == true)
            {
                if (entityExpiration != null)
                {
                    cacheManager.Add(GetQueryKey(tenantId, queryString), list, CacheItemPriority.Normal, null, entityExpiration);
                }
                else
                {
                    cacheManager.Add(GetQueryKey(tenantId, queryString), list);
                }
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (cacheManager != null)
                {
                    cacheManager.Flush();
                }
            }
        }
        #endregion

        private static string GetQueryKey(string tenantId, string queryString)
        {
            return tenantId + "-" + queryString;
        }

        private static string GetEntityKey(string tenantId, string kind, string entityId)
        {
            return tenantId + "-" + kind + "-" + entityId;
        }

        private void Initialize(string cacheManagerName, ICacheItemExpiration entitiesExpiration, bool cacheEntities, bool cacheQueries)
        {
            // No caching
            if (cacheManagerName == null)
            {
                return;
            }

            // Default manager
            if (String.IsNullOrEmpty(cacheManagerName))
            {
                cacheManager = CacheFactory.GetCacheManager();
            }
            else
            {
                // Specific cache manager in the config
                cacheManager = CacheFactory.GetCacheManager(cacheManagerName);
            }

            this.cacheEntitiesEnabled = cacheEntities;
            this.cacheQueriesEnabled = cacheQueries;

            this.entityExpiration = entitiesExpiration;
        }
    }
}
